package org.project.d.network.msg;


import lombok.ToString;
import org.project.d.network.Connection;
import org.project.d.network.DispatchManager;
import org.project.d.network.msg.serializer.DeSerialized;
import org.project.d.network.msg.serializer.NoSerialization;
import org.project.d.network.msg.serializer.Serialization;
import org.project.d.network.msg.serializer.SerializationManager;
import org.project.d.network.protocol.Packet;

import java.util.Optional;


/**
 *
 */
@ToString
public class BaseMessage implements Message {
    private static final byte STATUS_DECODED = 1;
    private static final byte STATUS_ENCODED = 2;

    //    private static final AtomicInteger ID_SEQ = new AtomicInteger();
    transient protected Packet packet;
    //    transient protected Connection connection;
    transient private byte status = 0;
    transient protected Connection connection;

    transient protected Object msgObj;

    transient protected CommandCode msgType;
    transient protected DeSerialized deSerialized;
    transient protected byte[] serializationBytes;

    public BaseMessage(Packet packet, Connection connection) {
        this.packet = packet;
        this.connection = connection;
        this.msgType= DispatchManager.getCommandCode(packet.getCmd());
    }
    public BaseMessage(Packet packet, Connection connection, CommandCode msgType) {
        this.packet = packet;
        this.connection = connection;
        this.msgType=msgType;
    }
    @Override
    public <T> T decodeBody(Class<T> tClass) {
        return decodeBody(tClass,null);
    }

    @Override
    public <T> T decodeBody(Class<T> tClass, DeSerialized deSerialized) {
        if ((status & STATUS_DECODED) == 0) {
            if (deSerialized !=null){
                this.deSerialized = deSerialized;
            }
            if (tClass==null){
                return (T) msgObj;
            }
            byte[] body = packet.getBody();
            if (body.length > 0) {
                byte serializationType = packet.getSerializationType();
                if (this.deSerialized !=null){
                    msgObj=this.deSerialized.deserialized(body,tClass);
                    status |= STATUS_DECODED;
                    return (T) msgObj;
                }
                Serialization serialization = SerializationManager.getSerializer(serializationType);
                if (!serialization.getClass().equals(NoSerialization.class)){
                    msgObj = serialization.deserialized(body,tClass);
                    status |= STATUS_DECODED;
                    return (T) msgObj;
                }
            }
        }
        return (T) msgObj;
    }

    @Override
    public byte[] encodeBody() {
        if ((status & STATUS_ENCODED) == 0) {
            status |= STATUS_ENCODED;
            byte serializationType = packet.getSerializationType();
            Serialization serialization = SerializationManager.getSerializer(serializationType);
            serializationBytes = serialization.serialized(msgObj);
        }
        return serializationBytes;
    }

    @Override
    public Packet getPacket() {
        return this.packet;
    }

    @Override
    public Connection getConnection() {
        return this.connection;
    }


    public CommandCode getMsgType() {
        return msgType;
    }

    @Override
    public <T> Optional<T> getBodyObj() {
        if (msgObj!=null){
            return Optional.of((T)msgObj);
        }
        return Optional.empty();
    }

}
